package com.yiwu.netty.handler;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSONUtil;
import com.yiwu.common.component.RedisComponent;
import com.yiwu.common.domain.dto.ChatMessageDTO;
import com.yiwu.common.domain.entity.ChatMessage;
import com.yiwu.common.enums.MessageTypeEnum;
import com.yiwu.common.enums.ResponseCode;
import com.yiwu.common.exception.BusinessException;
import com.yiwu.common.model.vo.chat.ChatMessageReqVO;
import com.yiwu.netty.util.NettyServerUtil;
import io.micrometer.common.util.StringUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import jakarta.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @description TODO
 * @date 2024/5/28 4:01
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Resource
    private RedisComponent redisComponent;

    @Resource
    private NettyServerUtil nettyServerUtil;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        Channel channel = ctx.channel();
        Attribute<String> attr = channel.attr(AttributeKey.valueOf(channel.id().toString()));
        String userId = attr.get();
        if (userId == null) {
            throw new BusinessException(ResponseCode.WS_ERROR);
        }
        try {
            ChatMessageReqVO chatMessageReqVO = JSONUtil.toBean(msg.text(), ChatMessageReqVO.class);
            MessageTypeEnum type = MessageTypeEnum.getType(chatMessageReqVO.getMessageType());
            if (type == null || !ArrayUtil.contains(new Integer[]{MessageTypeEnum.CHAT.getType(), MessageTypeEnum.MEDIA_CHAT.getType()}, chatMessageReqVO.getMessageType())) {
                throw new BusinessException(ResponseCode.MSGTYPE_ERROR);
            }
            ChatMessage chatMessage = new ChatMessage();
            BeanUtils.copyProperties(chatMessageReqVO, chatMessage);
            chatMessage.setSenderId(Long.parseLong(userId));
            chatMessage.setReceiverId(Long.parseLong(chatMessageReqVO.getReceiverId()));
            chatMessage.setStatus(0);
            if (type == MessageTypeEnum.CHAT) {
                chatMessage.setMessageType(2);
            } else if (type == MessageTypeEnum.MEDIA_CHAT) {
                //todo 媒体文件，图片、视频等
                chatMessage.setMessageType(3);
            }
            nettyServerUtil.sendMessage(chatMessage);
            ChatMessageDTO chatMessageDTO = new ChatMessageDTO();
            BeanUtils.copyProperties(chatMessage, chatMessageDTO);
            chatMessageDTO.setSenderId(chatMessage.getSenderId().toString());
            chatMessageDTO.setFileSize(chatMessage.getFileSize().toString());
            chatMessageDTO.setReceiverId(chatMessage.getReceiverId().toString());
            chatMessageDTO.setSendTime(chatMessage.getSendTime().toString());
            redisComponent.saveHeartBeat(chatMessage.getReceiverId().toString());
            channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonPrettyStr(chatMessageDTO)));
        } catch (Exception e) {
            throw new BusinessException(ResponseCode.PARAMETER_ERROR);
        }
        redisComponent.saveHeartBeat(userId);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("连接断开");
        nettyServerUtil.removeContext(ctx.channel());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("新的连接加入");
    }

    /**
     * @return void
     * @description 用户建立ws连接时，存入当前用户的channel
     * @date 2024/5/29 23:03
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete complete) {
            HttpHeaders entries = complete.requestHeaders();
            String token = entries.get("Authorization");
            String type = entries.get("SocketType");
            if (StringUtils.isEmpty(token)) {
                ctx.channel().close();
                return;
            }
            String userId = redisComponent.getUserId(token);
            if (StringUtils.isEmpty(userId)) {
                ctx.channel().close();
                return;
            }
            if (type != null) {
                nettyServerUtil.addContext(null, userId + type, ctx.channel());
            } else {
                String url = complete.requestUri();
                String sessionId = get2SessionId(url);
                if (StringUtils.isEmpty(sessionId)) {
                    ctx.channel().close();
                    return;
                }
                nettyServerUtil.addContext(sessionId, userId, ctx.channel());
            }
            log.info("token: {}", token);
        }
    }

    public String get2SessionId(String url) {
        if (StringUtils.isEmpty(url) || !url.contains("?")) return null;
        String[] queryParams = url.split("\\?");
        if (queryParams.length != 2) return null;

        String[] params = queryParams[1].split("=");
        if (params.length != 2) return null;
        return params[1];
    }

}
